/*
 * Copyright 2007-2008 Brian Cavalier
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bc.iterate;

import org.bc.iterate.io.PrintClosure;
import org.bc.iterate.io.PrintlnClosure;
import org.bc.iterate.io.StreamView;
import org.bc.iterate.view.*;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;

/**
 * The core class of the iterate package containing static methods for iterating, creating and
 * invoking {@link Closure}s, {@link Transform}s, {@link Inject}s, {@link Test}s, {@link MapClosure}s,
 * {@link MapInject}s, etc.
 *
 * @author Brian Cavalier
 */
@SuppressWarnings({"StaticMethodOnlyUsedInOneClass", "IOResourceOpenedButNotSafelyClosed"})
public class Iterate
{
    //
    // Each applies the supplied Closure to all elements or a subset
    // matched by the supplied Test
    //


    /**
     * Main iterator that invokes the supplied Closure for each item provided by the supplied Iterable.
     *
     * @param c Items over which to iterate
     * @param b the Closure to invoke for each matching item.
     */
    public static <T> void each(Iterable<? extends T> c, Closure<? super T> b)
    {
        for (T item : c) {
            b.apply(item);
        }
    }

    /**
     * Main iterator that invokes the supplied Closure for each item for which the supplied Test evaluates to true.
     *
     * @param c Items over which to iterate
     * @param t Test to apply to each item.  Only items for which t evaluates to true will be passed to the Closure.
     * @param b the Closure to invoke for each matching item.
     */
    public static <T> void each(Iterable<? extends T> c, Test<? super T> t, Closure<? super T> b)
    {
        for (T item : c) {
            if (t.go(item)) {
                b.apply(item);
            }
        }
    }

    /**
     * Main iterator that invokes the supplied Closure for each item for which t.compareTo(item) == 0.
     *
     * @param c Items over which to iterate
     * @param t Comparable to compare to each item.  Only items for which t.compareTo(item) == 0 will be passed to the
     *          Closure.
     * @param b the Closure to invoke for each matching item.
     */
    public static <T> void each(Iterable<? extends T> c, Comparable<? super T> t, Closure<? super T> b)
    {
        for (T item : c) {
            if (t.equals(item)) {
                b.apply(item);
            }
        }
    }

    /**
     * @param results {@link java.util.Collection} to which to add items.
     * @return a {@link Closure} that will add items to the supplied {@link java.util.Collection}
     */
    public static <T> Closure<T> collect(Collection<T> results)
    {
        return new CollectionClosure<T>(results);
    }

    //
    // inject() invokes an Inject with the second argument
    //

    /**
     * Inject is like each but allows an additional parameter to be <i>injected</i> into the iteration by passing it to
     * the supplied Inject.
     *
     * @param c Items over which to iterate
     * @param e Extra item to pass to the supplied Inject
     * @param i the Inject to invoke for each item.
     * @return e
     */
    public static <E, T> E inject(Iterable<? extends T> c, E e, Inject<E, ? super T> i)
    {
        for (T item : c) {
            i.go(e, item);
        }

        return e;
    }

    /**
     * Inject is like each but allows an additional parameter to be <i>injected</i> into the iteration by passing it to
     * the supplied Inject.
     *
     * @param c Items over which to iterate
     * @param e Extra item to pass to the supplied Inject
     * @param t Only itmes from c for which this evaluates to true will be passed on to the Inject.
     * @param i the Inject to invoke for each matching item.
     * @return e
     */
    public static <E, T> E inject(Iterable<? extends T> c, E e, Test<? super T> t, Inject<E, ? super T> i)
    {
        for (T item : c) {
            if (t.go(item)) {
                i.go(e, item);
            }
        }

        return e;
    }

    /**
     * Inject is like each but allows an additional parameter to be <i>injected</i> into the iteration by passing it to
     * the supplied Inject.
     *
     * @param c Items over which to iterate
     * @param e Extra item to pass to the supplied Inject
     * @param t Only itmes from c for which t.compareTo(item) == 0 will be passed on to the Inject.
     * @param i the Inject to invoke for each matching item.
     * @return e
     */
    public static <E, T> E inject(Iterable<? extends T> c, E e, Comparable<? super T> t, Inject<E, ? super T> i)
    {
        for (T item : c) {
            if (t.equals(item)) {
                i.go(e, item);
            }
        }

        return e;
    }    

    /**
     * Map version of each() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param c the MapClosure to invoke for each key-value pair
     */
    public static <K, T> void each(Map<? extends K, ? extends T> m, MapClosure<? super K, ? super T> c)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            c.go(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Map version of each() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param t the Test to apply to each Map key.  Only keys for which t evaluates to true will be passed to the
     *          MapClosure.
     * @param c the MapClosure to invoke for each key-value pair
     */
    public static <K, T> void each(Map<? extends K, ? extends T> m,
                                   Test<? super K> t,
                                   MapClosure<? super K, ? super T> c)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            K key = entry.getKey();
            if (t.go(key)) {
                c.go(key, entry.getValue());
            }
        }
    }

    /**
     * Map version of each() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param c the MapClosure to invoke for each key-value pair
     */
    public static <K, T> void each(Map<? extends K, ? extends T> m,
                                   Comparable<? super K> t,
                                   MapClosure<? super K, ? super T> c)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            K key = entry.getKey();
            if (t.compareTo(key) == 0) {
                c.go(key, entry.getValue());
            }
        }
    }

    /**
     * Creates a {@link Test} that allows only items within the range bounded by low and high.
     *
     * @param low  Lower bound
     * @param high Upper bound
     *
     * @see Test
     */
    public static <T> Test<T> in(Comparable<T> low, Comparable<T> high)
    {
        return new Range<T>(low, high);
    }

    /**
     * Creates an Iterator over the integers from 0 to n.
     *
     * @param n upper bound
     *
     * @return IntegerRangeView from 0 to n
     */
    public static IntegerRangeView n(int n)
    {
        return new IntegerRangeView(0, n);
    }

    /**
     * Creates an Iterator over the integers from start to Integer.MAX_VALUE.
     *
     * @param start lower bound
     *
     * @return IntegerRangeView from 0 to Integer.MAX_VALUE
     */
    public static IntegerRangeView from(int start)
    {
        return new IntegerRangeView().from(start);
    }

    /**
     * Creates an Iterator over the integers from start to end.
     *
     * @param start lower bound
     * @param end   upper bound
     *
     * @return IntegerRangeView from start to end
     */
    public static IntegerRangeView range(int start, int end)
    {
        return new IntegerRangeView(start, end);
    }

    /**
     * Creates an Iterator over the integers from start to end, stepping by step.
     *
     * @param start lower bound
     * @param end   upper bound
     * @param step  amount to step with each call to next
     *
     * @return IntegerRangeView from start to end that steps by step.
     */
    public static StepIntegerRangeView range(int start, int end, int step)
    {
        return new StepIntegerRangeView(start, end, step);
    }

    /**
     * Creates an Iterator over the integers from 0 to Integer.MAX_VALUE, stepping by step.
     *
     * @param step amount to step with each call to next()
     *
     * @return StepIntegerRangeView from 0 to Integer.MAX_VALUE, that steps by step.
     */
    public static StepIntegerRangeView by(int step)
    {
        return new StepIntegerRangeView().by(step);
    }

    /**
     * Creates an Adapter for an existing Iterator, allowing it to be used in calls to the various Iterate methods that
     * require an Iterable.
     *
     * @return an IteratorAdapter wrapping the supplied Iterator
     *
     * @see IteratorAdapter
     * @see Iterable
     */
    public static <T> Iterable<T> of(Iterator<T> i)
    {
        return new IteratorAdapter<T>(i);
    }

    /**
     * Creates an Adapter for an array, allowing it to be used in calls to the various Iterate methods that require an
     * Iterable.
     *
     * @return an ArrayView wrapping the supplied Iterator
     *
     * @see org.bc.iterate.view.ArrayView
     * @see Iterable
     */
    public static <T> Iterable<T> of(T... items)
    {
        return new ArrayView<T>(items);
    }

    /**
     * Creates a Grep Test using the specified Pattern.
     *
     * @param p Pattern to match
     *
     * @return Grep that will match the supplied Pattern.
     *
     * @see Grep
     */
    public static Grep grep(Pattern p)
    {
        return new Grep(p);
    }

    /**
     * Creates a Grep Test that compiles the specified regular expression Pattern.
     *
     * @param pattern Pattern to compile and match
     *
     * @return Grep that will match the compiled Pattern.
     *
     * @see Grep
     */
    public static Grep grep(String pattern)
    {
        return grep(Pattern.compile(pattern));
    }

    /**
     * Creates a Grep Test that calls toString() on the supplied object, and Compiles the Result Into a Regular
     * Expression Pattern.
     *
     * @param pattern Object on which toString() will be invoked.  The resulting String will be compiled into a regular
     *                expression Pattern.
     *
     * @return Grep That Will Match the Compiled Pattern.
     *
     * @see Grep
     */
    public static Grep grep(Object pattern)
    {
        return grep(pattern.toString());
    }

    /**
     * Creates an OGrep Test using the specified Pattern.
     *
     * @param p Pattern to match
     *
     * @return Grep that will match the supplied Pattern.
     *
     * @see Grep
     */
    public static GrepObject ogrep(Pattern p)
    {
        return new GrepObject(p);
    }

    /**
     * Creates an OGrep Test that compiles the specified regular expression Pattern.
     *
     * @param pattern Pattern to compile and match
     *
     * @return Grep that will match the compiled Pattern.
     *
     * @see Grep
     */
    public static GrepObject ogrep(String pattern)
    {
        return ogrep(Pattern.compile(pattern));
    }

    /**
     * Creates an OGrep Test that calls toString() on the supplied object, and Compiles the Result Into a Regular
     * Expression Pattern.
     *
     * @param pattern Object on which toString() will be invoked.  The resulting String will be compiled into a regular
     *                expression Pattern.
     *
     * @return Grep That Will Match the Compiled Pattern.
     *
     * @see Grep
     */
    public static GrepObject ogrep(Object pattern)
    {
        return ogrep(pattern.toString());
    }

    /**
     * @return a {@link ChompTransform}
     */
    public static Transform<String> chomp()
    {
        return new ChompTransform();
    }

    /**
     * @return a {@link Transform} that will trim leading and trailing whitespace from {@code String}s 
     */
    public static Transform<String> trim()
    {
        return new TrimTransform();
    }

    /**
     * Creates a {@link Sed} {@link Transform} that replaces all matches to {@code pattern} with {@code replacement} 
     * @param p {@code Pattern} to match
     * @param replacement {@code String} with which to replace all occurrences of {@code pattern}
     * @return
     */
    public static Sed sed(Pattern p, String replacement)
    {
        return new Sed(p, replacement);
    }

    /**
     * Creates a {@link Sed} {@link Transform} that replaces all matches to {@code pattern} with {@code replacement}
     * @param pattern regex pattern {@code String} to match
     * @param replacement {@code String} with which to replace all occurrences of {@code pattern}
     * @return
     */
    public static Sed sed(String pattern, String replacement)
    {
        return new Sed(Pattern.compile(pattern), replacement);
    }

    /**
     * @return a {@link CollectionInject}
     */
    public static <T> Inject<Collection, T> collect()
    {
        return new CollectionInject<T>();
    }

    //
    // IO Iterators
    //

    /**
     * Make an iterator that reads text lines from the file at the specified path.
     *
     * @param file Path of file over which to iterate.
     *
     * @return StreamView
     *
     * @throws IOException
     * @see org.bc.iterate.io.StreamView
     */
    public static StreamView file(String file) throws IOException
    {
        return new StreamView(new FileReader(file), true);
    }

    /**
     * Make an iterator that reads text lines from the supplied file.
     *
     * @param file File over which to iterate
     *
     * @return StreamView
     *
     * @throws IOException
     * @see org.bc.iterate.io.StreamView
     */
    public static StreamView file(File file) throws IOException
    {
        return new StreamView(new FileReader(file), true);
    }

    /**
     * Make an iterator that reads text lines from the supplied InputStream.
     *
     * @param in InputStream from which to read lines.
     *
     * @return StreamView
     *
     * @see org.bc.iterate.io.StreamView
     */
    public static StreamView stream(InputStream in)
    {
        return new StreamView(in, false);
    }

    /**
     * Make an iterator that reads text lines from the supplied Reader.
     *
     * @param in Reader from which to read lines.
     *
     * @return StreamView
     *
     * @see org.bc.iterate.io.StreamView
     */
    public static StreamView stream(Reader in)
    {
        return new StreamView(in, false);
    }

    /**
     * Make an iterator that reads text lines from the supplied url.
     *
     * @param url URL from which to read lines.
     *
     * @return StreamView
     *
     * @throws MalformedURLException if the supplied url cannot be interpreted by <code>java.net.URL</code>.
     * @throws IOException           if an error occurs attempting to open a connection to the specified url.
     */
    public static StreamView url(String url) throws IOException
    {
        return url(new URL(url));
    }

    /**
     * Make an iterator that reads text lines from the supplied <code>java.net.URL</code>.
     *
     * @param url URL from which to read lines.
     *
     * @return StreamView
     *
     * @throws IOException if an error occurs attempting to open a connection to the specified url.
     */
    public static StreamView url(URL url) throws IOException
    {
        return new StreamView(url.openStream(), true);
    }

    /**
     * Make an iterator that reads text lines from the supplied <code>java.net.URL</code>.
     *
     * @param uri URL from which to read lines.
     *
     * @return StreamView
     *
     * @throws IOException if an error occurs attempting to open a connection to the specified url.
     */
    public static StreamView url(URI uri) throws IOException
    {
        return url(uri.toURL());
    }

    /**
     * Make a Closure that prints any Object to <code>System.out</code>
     *
     * @return PrintClosure
     *
     * @see PrintClosure
     */
    public static  Closure println()
    {
        return new PrintlnClosure(new PrintWriter(System.out, true));
    }

    /**
     * Make a Closure that prints any Object to <code>System.out</code>
     *
     * @return PrintClosure
     *
     * @see PrintClosure
     */
    public static PrintClosure print()
    {
        return print(System.out, true);
    }

    /**
     * Make a Closure that prints any Object to the specified file.
     *
     * @param file Path of file
     *
     * @return PrintClosure
     *
     * @throws IOException if the specified file cannot be opened for writing.
     */
    public static PrintClosure print(String file) throws IOException
    {
        return print(new FileWriter(file), false);
    }

    /**
     * Make a Closure that prints any Object to the specified OutputStream.
     *
     * @param p     OutputStream to which to print
     * @param flush if set to true, the OutputStream will be flushed after each Object is printed.  If set to false, the
     *              OutputStream will not be forced to flush, and thus will flush according to its internal
     *              implementation.
     *
     * @return PrintClosure
     */
    public static PrintClosure print(OutputStream p, boolean flush)
    {
        return new PrintClosure(new PrintWriter(p, flush));
    }

    /**
     * Make a Closure that prints any Object to the specified Writer.
     *
     * @param p     Writer to which to print
     * @param flush if set to true, the Writer will be flushed after each Object is printed.  If set to false, the
     *              Writer will not be forced to flush, and thus will flush according to its internal implementation.
     *
     * @return PrintClosure
     */
    public static PrintClosure print(Writer p, boolean flush)
    {
        return new PrintClosure(new PrintWriter(p, flush));
    }

    /**
     * Make a {@link Transform} that will apply all of the supplied {@link Transform}s, in sequence.
     * @param t one or more {@link Transform}s to be applied in sequence.
     * @return {@link Transform}
     */
    public static <T> TransformChain<T> chain(Transform<T>... t)
    {
        return new TransformChain<T>(t);
    }

    /**
     * Make a {@link Closure} that will first apply the supplied {@link Transform} and the execute the supplied
     * {@link org.bc.iterate.Closure}
     * @param tr {@link Transform} to apply
     * @param c {@link Closure} to execute
     * @return {@link Closure}
     */
    public static <T> Closure<T> chain(Transform<T> tr, Closure<T> c)
    {
        return new TransformClosure<T>(tr, c);
    }
    
    /**
     * Make a {@link Test} that will first apply the supplied {@link Transform} and then the supplied
     * {@link Test} on the transformed value
     * @param tr {@link Transform} to apply prior to the {@link Test}
     * @param t {@link Test} to apply after transforming.
     * @return {@link Test}
     */
    public static <T> Test<T> chain(Transform<T> tr, Test<T> t)
    {
        return new TransformTest<T>(tr, t);
    }

    /**
     * Map version of inject() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param i the MapInject to invoke for each key-value pair
     * @return e
     */
    public static <E, K, T> E inject(Map<? extends K, ? extends T> m, E e, MapInject<E, ? super K, ? super T> i)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            i.go(e, entry.getKey(), entry.getValue());
        }

        return e;
    }

    /**
     * Map version of inject() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param t the Test to apply to each Map key.  Only keys for which t evaluates to true will be passed to the
     *          MapInject.
     * @param i the MapInject to invoke for each key-value pair
     * @return e
     */
    public static <E, K, T> E inject(Map<? extends K, ? extends T> m,
                                     E e,
                                     Test<? super K> t,
                                     MapInject<E, ? super K, ? super T> i)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            K key = entry.getKey();
            if (t.go(key)) {
                i.go(e, key, entry.getValue());
            }
        }

        return e;
    }

    /**
     * Map version of inject() that passes each key and value to the supplied MapClosure.
     *
     * @param m the Map over which to iterate
     * @param t the Comparable to compare to each Map key.  Only keys for which t.compareTo(key) == 0 will be passed to
     *          the MapInject.
     * @param i the MapInject to invoke for each key-value pair
     */
    public static <E, K, T> E inject(Map<? extends K, ? extends T> m,
                                     E e,
                                     Comparable<? super K> t,
                                     MapInject<E, ? super K, ? super T> i)
    {
        for (Map.Entry<? extends K, ? extends T> entry : m.entrySet()) {
            K key = entry.getKey();
            if (t.compareTo(key) == 0) {
                i.go(e, key, entry.getValue());
            }
        }

        return e;
    }

    //
    // Views on Iterables
    //

    /**
     * Makes a {@link View} of the supplied {@link Iterable} that appears to only contain items for which the
     * supplied {@link Test} evaluates to {@code true}.
     * @param i
     * @param t
     * @return
     */
    public static <T> Iterable<T> each(Iterable<? extends T> i, Test<? super T> t)
    {
        return new TestView<T>(i, t);
    }

    /**
     * Makes a {@link View} of the supplied {@link Iterable} that appears to only contain items for which the
     * the supplied {@link Comparable#compareTo(Object)} {@code == 0}.
     * @param i
     * @param t
     * @return
     */
    public static <T> Iterable<T> each(Iterable<? extends T> i, Comparable<? super T> t)
    {
        return new ComparableView<T>(i, t);
    }

    /**
     * Make a {@link View} of the supplied {@link InputStream} containing {@code String}s generated by tokenizing
     * the {@link InputStream} using the supplied {@code delimiterPattern}
     * @param in {@link InputStream} to split
     * @param delimiterPattern delimiter to use in splitting {@code in}
     * @return {@link Iterable} ({@link View})
     */
    public static Iterable<String> split(InputStream in, String delimiterPattern)
    {
        return of(new Scanner(in).useDelimiter(delimiterPattern));
    }

    /**
     * Make a {@link View} of the supplied {@code String} containing {@code String}s generated by tokenizing
     * the {@code String} using the supplied {@code delimiterPattern}
     * @param in {@code String} to split
     * @param delimiterPattern delimiter to use in splitting {@code in}
     * @return {@link Iterable} ({@link View})
     */
    public static Iterable<String> split(String in, String delimiterPattern)
    {
        return of(new Scanner(in).useDelimiter(delimiterPattern));
    }

    /**
     * Make a {@link View} of the supplied {@link InputStream} containing {@link MatchResult}s for all matches
     * of the supplied regex {@code pattern}.
     * @param in {@link InputStream} to match
     * @param pattern regex {@code String} to match
     * @return {@link Iterable} ({@link View})
     */
    public static Iterable<MatchResult> scan(InputStream in, String pattern)
    {
        return new ScannerView(in).setPattern(pattern);
    }

    public static Iterable<MatchResult> scan(InputStream in, Pattern pattern)
    {
        return new ScannerView(in).setPattern(pattern);
    }

    /**
     * Make a {@link View} containing each of the groups matches by the supplied {@link MatchResult}
     * @param m {@link MatchResult} whose groups will be made iterable
     * @return {@link Iterable} ({@link View})
     */
    public static Iterable<String> matches(MatchResult m)
    {
        return new MatchResultView(m);
    }


    /**
     * Invokes the supplied Closure n times, passing the current number of completed iterations as the Closure
     * parameter.  For the first iteration, the {@link Closure} will be passed {@code 0}.
     *
     * @param n int
     * @param b Closure<Integer>
     */
    public static void times(int n, Closure<Integer> b)
    {
        for (int i = 0; i < n; ++i) {
            b.apply(i);
        }
    }

    /**
     * Invokes the supplied Closure once for each integer in the supplied range.
     *
     * @param from starting integer value, inclusive.  This is the first value that will be passed to the Closure.
     * @param to   ending integer value, <em>exclusive</em>.  This value will never be passed to the Closure.
     * @param b    Closure to invoke for each integer in the range.
     */
    public static void range(int from, int to, Closure<Integer> b)
    {
        for (int i = from; i < to; ++i) {
            b.apply(i);
        }
    }

    /**
     * Invokes the supplied Closure once for each integer in the supplied range, stepping by the supplied amount.
     *
     * @param from starting integer value, inclusive.  This is the first value that will be passed to the Closure.
     * @param to   ending integer value, <em>exclusive</em>.  This value will never be passed to the Closure.
     * @param by   amount by which the integer will be incremented in each call to the Closure.  For example, if from =
     *             10, end = 21, and by = 5, the Closure will be invoked with the integers 10, 15, and 20.
     * @param b    Closure to invoke for each integer in the range.
     */
    public static void range(int from, int to, int by, Closure<Integer> b)
    {
        for (int i = from; i < to; i += by) {
            b.apply(i);
        }
    }

    public static <T> void until(Iterable<? extends T> c, Comparable<? super T> t, Closure<? super T> b)
    {
        for (T item : c) {
            if (t.compareTo(item) <= 0) {
                break;
            }
            b.apply(item);
        }
    }

    public static <T> void until(Iterable<? extends T> c, Test<? super T> t, Closure<? super T> b)
    {
        for (T item : c) {
            if (t.go(item)) {
                break;
            }
            b.apply(item);
        }
    }

    //
    // Transforming variants of each() apply the supplied Transform to all
    // elements or a subset matched by the supplied Test and store
    // the results in a new Collection.
    //

    /**
     * Applies the supplied {@link Transform} to each element in {@code c} and returns a {@link Collection}
     * containing all of the transformed results.
     *
     * @param c {@link Iterable} of elements to transform
     * @param b {@link Transform} to apply to each element
     * @return {@link Collection} containing the transformed elements.  Will always contain the same number of
     * elements as {@code c}
     */
    public static <T> Collection<T> each(Iterable<T> c, Transform<T> b)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            nc.add(b.apply(item));
        }
        return nc;
    }

    /**
     * Applies the supplied {@link Transform} to each element in {@code c} for which the supplied
     * {@link Test} evaluates to true and returns a {@link Collection} containing all of the transformed results.
     *
     * @param c {@link Iterable} of elements to transform
     * @param b {@link Transform} to apply to each element
     * @return {@link Collection} containing the transformed elements.  Will contain at most the same number of
     * elements as {@code c}.
     */
    public static <T> Collection<T> each(Iterable<T> c, Test<T> t, Transform<T> b)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (t.go(item)) {
                nc.add(b.apply(item));
            }
        }
        return nc;
    }

    /**
     * Applies the supplied {@link Transform} to each element in {@code c} for which the supplied
     * {@link Comparable#equals(Object)} evaluates to true and returns a {@link Collection} containing all
     * of the transformed results.
     *
     * @param c {@link Iterable} of elements to transform
     * @param b {@link Transform} to apply to each element
     * @return {@link Collection} containing the transformed elements.  Will contain at most the same number of
     * elements as {@code c}.
     */
    public static <T> Collection<T> each(Iterable<T> c, Comparable<T> t, Transform<T> b)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (t.equals(item)) {
                nc.add(b.apply(item));
            }
        }
        return nc;
    }

    //
    // Select/reject return new Collections containing subset
    //

    public static <T> Collection<T> select(Iterable<? extends T> c, Test<? super T> t)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (t.go(item)) {
                nc.add(item);
            }
        }
        return nc;
    }

    public static <T> Collection<T> select(Iterable<? extends T> c, Comparable<? super T> t)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (t.compareTo(item) == 0) {
                nc.add(item);
            }
        }
        return nc;
    }

    public static <T> Collection<T> reject(Iterable<? extends T> c, Test<? super T> t)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (!t.go(item)) {
                nc.add(item);
            }
        }
        return nc;
    }

    public static <T> Collection<T> reject(Iterable<? extends T> c, Comparable<? super T> t)
    {
        ArrayList<T> nc = new ArrayList<T>(20);
        for (T item : c) {
            if (t.compareTo(item) != 0) {
                nc.add(item);
            }
        }
        return nc;
    }

    //
    // Retain/remove modify the Collection in place
    //

    public static <T> void retain(Collection<? extends T> c, Test<? super T> t)
    {
        for (T item : c) {
            if (!t.go(item)) {
                //noinspection SuspiciousMethodCalls
                c.remove(item);
            }
        }
    }

    public static <T> void remove(Collection<? extends T> c, Test<? super T> t)
    {
        for (T item : c) {
            if (t.go(item)) {
                //noinspection SuspiciousMethodCalls
                c.remove(item);
            }
        }
    }

    // A more optimized version for Lists using ListIterator
    public static <T> void retain(List<? extends T> c, Test<? super T> t)
    {
        ListIterator<? extends T> i = c.listIterator();
        while (i.hasNext()) {
            T item = i.next();
            if (!t.go(item)) {
                i.remove();
            }
        }
    }

    // A more optimized version for Lists using ListIterator
    public static <T> void remove(List<? extends T> c, Test<? super T> t)
    {
        ListIterator<? extends T> i = c.listIterator();
        while (i.hasNext()) {
            T item = i.next();
            if (t.go(item)) {
                i.remove();
            }
        }
    }

    //
    // Counting and membership tests
    //

    /**
     * Counts the number of items for which the supplied Test evaluates to true.
     *
     * @param c Items over which to iterate.
     * @param t Test to apply to each item.
     *
     * @return number of matching items.
     */
    public static <T> int count(Iterable<? extends T> c, Test<? super T> t)
    {
        int count = 0;
        for (T item : c) {
            if (t.go(item)) {
                ++count;
            }
        }
        return count;
    }


    /**
     * Determines if the supplied Test evaluates to true for all items.
     *
     * @param c Items over which to iterate
     * @param t Test to apply to each item
     *
     * @return true iff the Test evaluates to true for all items.
     */
    public static <T> boolean all(Iterable<? extends T> c, Test<? super T> t)
    {
        for (T item : c) {
            if (!t.go(item)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Determines if the supplied Test evaluates to true for at least one item.
     *
     * @param c Items over which to iterate
     * @param t Test to apply to each item
     *
     * @return true iff the Test evaluates to true for at least one item.
     */
    public static <T> boolean has(Iterable<? extends T> c, Test<? super T> t)
    {
        for (T item : c) {
            if (t.go(item)) {
                return true;
            }
        }

        return false;
    }


    /**
     * Finds the first item for which the supplied test evaluates to true.
     *
     * @param c Items over which to iterate
     * @param t Test to apply to each item
     *
     * @return first matching item, or null if none match.
     */
    public static <T> T first(Iterable<? extends T> c, Test<? super T> t)
    {
        for (T item : c) {
            if (t.go(item)) {
                return item;
            }
        }

        return null;
    }

    /**
     * Finds the last item for which the supplied test evaluates to true. This is implemented on for Lists since finding
     * the <i>last</i> item implies an ordering.
     *
     * @param c List over which to iterate
     * @param t Test to apply to each item
     *
     * @return last matching item, or null if none match.
     */
    public static <T> T last(List<? extends T> c, Test<? super T> t)
    {
        for (int i = c.size() - 1; i >= 0; i--) {
            T item = c.get(i);
            if (t.go(item)) {
                return item;
            }
        }

        return null;
    }

    //
    // Floor returns the greatest item that is less than or equal to the
    // supplied Comparable
    // Ceiling returns the least item that is greater than or equal to the
    // supplied Comparable.
    //
    // These both could be binary searches

    /**
     * Floor returns the greatest item that is less than or equal to the supplied Comparable
     */
    public static <T> T floor(SortedSet<? extends T> c, Comparable<? super T> t)
    {
        T ret = null;
        for (T item : c) {
            if (t.compareTo(item) < 0) {
                return ret;
            }
            ret = item;
        }

        return null;
    }

    /**
     * Ceiling returns the least item that is greater than or equal to the supplied Comparable.
     */
    public static <T> T ceiling(SortedSet<? extends T> c, Comparable<? super T> t)
    {
        for (T item : c) {
            if (t.compareTo(item) <= 0) {
                return item;
            }
        }

        return null;
    }

    //
    // Joining Collection elements together into Strings
    //
    /**
     * Compiled default separator used for join() methods
     */
    private final static String DEFALT_JOIN_SEPARATOR = ",";


    /**
     * User-settable default separator used for join() methods
     *
     * @see #join(Iterable)
     */
    @SuppressWarnings({"PublicField"})
    public static String defaultJoinSeparator = DEFALT_JOIN_SEPARATOR;

    /**
     * Joins all items together as a String, using <code>{@link #defaultJoinSeparator}</code> between each item.
     *
     * @param c Items to join
     *
     * @return a String of all the items, separated by <code>{@link #defaultJoinSeparator}</code>
     *
     * @see #defaultJoinSeparator
     */
    public static <T> String join(Iterable<T> c)
    {
        return Iterate.join(c, defaultJoinSeparator);
    }

    /**
     * Joins all items together as a String, using the supplied separator between each item.
     *
     * @param c   Items to join
     * @param sep the separator to include between items
     *
     * @return a String of all the items, separated by the supplied separator.
     */
    public static <T> String join(Iterable<T> c, CharSequence sep)
    {
        Iterator<T> iter = c.iterator();
        if (iter.hasNext()) {
            StringBuilder buf = new StringBuilder(iter.next().toString());
            while (iter.hasNext()) {
                buf.append(sep).append(iter.next().toString());
            }
            return buf.toString();
        }

        return "";
    }

    /**
     * Joins all items together as a String, using <code>{@link #defaultJoinSeparator}</code> between each item.
     *
     * @param a Items to join
     *
     * @return a String of all the items, separated by <code>{@link #defaultJoinSeparator}</code>
     *
     * @see #defaultJoinSeparator
     */
    public static <T> String join(T... a)
    {
        return join(of(a), defaultJoinSeparator);
    }

    /**
     * Joins all items together as a String, using the supplied separator between each item.
     *
     * @param a   Items to join
     * @param sep the separator to include between items
     *
     * @return a String of all the items, separated by the supplied separator.
     */
    public static <T> String join(T[] a, CharSequence sep)
    {
        return join(of(a), sep);
    }
    
    //
    // Printing
    //

    public static <T> void print(Iterable<T> c)
    {
        for (T item : c) {
            System.out.print(item);
        }
    }

    public static <T> void println(Iterable<T> c)
    {
        for (T item : c) {
            System.out.println(item);
        }
    }

    public static <T> void print(Iterable<T> c, CharSequence sep)
    {
        Iterator<T> iter = c.iterator();
        if (iter.hasNext()) {
            System.out.append(iter.next().toString());
            while (iter.hasNext()) {
                System.out.append(sep).append(iter.next().toString());
            }
        }
    }

    public static <T> void println(Iterable<T> c, CharSequence sep)
    {
        print(c, sep);
        System.out.println();
    }

    public static <T> void print(Iterable<T> c, CharSequence sep, CharSequence bracket)
    {
        System.out.print(bracket.charAt(0));
        System.out.print(join(c, sep));
        System.out.print(bracket.charAt(1));
    }

    public static <T, B> void print(Iterable<T> c, CharSequence sep, B... bracket)
    {
        System.out.print(bracket[0]);
        System.out.print(join(c, sep));
        System.out.print(bracket[1]);
    }


}
